from __future__ import annotations
import random
import sys
from PyQt5.QtCore import QObject, pyqtSignal, QTimer, Qt
from PyQt5.QtGui import QPainter, QColor
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QWidget, QPushButton
from Rectangle import Rectangle
import platform

x_press_i = 3
# 如果要是在鲁班猫上面才执行引脚按键相关的代码
if not platform.system() == "Windows":
    import gpiod

    print("windows")
    # 按键按下循环三次后会重复按下

    # GPIO1_A0
    ROTATE_LINE_OFFSET = 0  # GPIO1_A0
    DOWN_LINE_OFFSET = 1  # GPIO1_A1
    LEFT_LINE_OFFSET = 12  # GPIO3_B4
    RIGHT_LINE_OFFSET = 20  # GPIO3_C4
    TO_BOTTOM_LINE_OFFSET = 21  # GPIO3_C5

    # 获取gpio0 gpio3
    chip0 = gpiod.Chip("1", gpiod.Chip.OPEN_BY_NUMBER)
    chip3 = gpiod.Chip("3", gpiod.Chip.OPEN_BY_NUMBER)

    # 获取对应引脚
    chip0_rotate = chip0.get_line(ROTATE_LINE_OFFSET)
    chip0_down = chip0.get_line(DOWN_LINE_OFFSET)
    chip3_left = chip3.get_line(LEFT_LINE_OFFSET)
    chip3_right = chip3.get_line(RIGHT_LINE_OFFSET)
    chip3_to_bottom = chip3.get_line(TO_BOTTOM_LINE_OFFSET)
    # 引脚设置输入模式
    chip0_rotate.request(consumer="BUTTON", type=gpiod.LINE_REQ_DIR_IN)
    chip0_down.request(consumer="BUTTON", type=gpiod.LINE_REQ_DIR_IN)
    chip3_left.request(consumer="BUTTON", type=gpiod.LINE_REQ_DIR_IN)
    chip3_right.request(consumer="BUTTON", type=gpiod.LINE_REQ_DIR_IN)
    chip3_to_bottom.request(consumer="BUTTON", type=gpiod.LINE_REQ_DIR_IN)


class KeySignal(QObject):
    # 到底信号
    to_bottom = pyqtSignal(list)
    # 左信号
    left = pyqtSignal()
    # 右信号
    right = pyqtSignal()
    # 下信号
    down = pyqtSignal(list)
    # 旋转信号
    rotate = pyqtSignal()


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        # 左按键正在被按下
        # 会有一个每隔固定时间调用一次的函数，如果按键正在按下就发一个对应的信号
        # 但是左右移动希望按下之后立即发一次，之后 稍微等一下再连续移动
        # 所以按下之后先把x_press_i设置为n，之后每次回调先将x_press_i--之后如果等于零或者n才发送信号
        self.left_pressing = False
        self.left_press_i = 0

        self.right_pressing = False
        self.right_press_i = 0

        self.down_pressing = False

        self.bottom_pressing = False
        self.bottom_first_pressed = False

        self.rotate_pressing = False
        self.rotate_first_pressed = False

        self.button = None
        self.label = None

        self.repaint_timer = None
        self.down_timer = None
        self.key_timer = None
        self.pin_button_timer = None

        self.relative_x = 0
        self.relative_y = 0

        self.next_Relative_x = 0
        self.next_Relative_y = 0

        self.next_type = 0
        self.now_type = 0
        self.state = 0
        self.dropped_tetris: list[Rectangle] = []
        self.moving_tetris: list[Rectangle] = []
        self.next_tetris: list[Rectangle] = []
        self.shadow_tetris: list[Rectangle] = []
        self.signals = None

        self.gameState = 0
        self.pauseSignal = pyqtSignal
        self.continueSignal = pyqtSignal

        self.initUI()
        self.initGame()

    def initGame(self):
        self.signals: KeySignal = KeySignal()
        self.signals.to_bottom.connect(self.to_bottem)

        self.moving_tetris.clear()
        self.next_tetris.clear()
        self.dropped_tetris.clear()

        # 游戏状态0进行中1暂停2结束
        self.gameState = 0

        # 正在移动中的俄罗斯方块和已经落下的方块
        for i in range(4):
            self.moving_tetris.append(Rectangle(-1, -1))
            self.next_tetris.append(Rectangle(-1, -1))
        self.relative_x = 3
        self.relative_y = -2
        self.now_type = random.randint(0, 6)
        self.next_type = random.randint(0, 6)
        self.state = 0
        self.to_state(self.relative_x, self.relative_y, self.now_type, self.state, self.moving_tetris)
        self.to_state(13, 5, self.next_type, 0, self.next_tetris)

        # 设置计时器，每50毫秒触发一次重绘
        self.repaint_timer = QTimer(self)
        self.repaint_timer.timeout.connect(self.update)  # 调用 update 方法触发重绘
        self.repaint_timer.start(50)

        # 设置计时器，每1秒触发掉落一次
        self.down_timer = QTimer(self)
        self.down_timer.timeout.connect(lambda: self.moveDown(self.moving_tetris))  # 向下移动一次
        self.down_timer.setSingleShot(True)
        self.down_timer.start(1000)

        if not platform.system() == "Windows":
            self.pin_button_timer = QTimer(self)
            self.pin_button_timer.timeout.connect(self.pinButtonTimer)
            self.pin_button_timer.start(100)

        # 处理按键的回调，每一定时间判断一次按键状态，如果按键按下做出对应操作
        self.key_timer = QTimer(self)
        self.key_timer.timeout.connect(self.keyHandle)
        self.key_timer.start(50)

        self.signals.down.connect(self.moveDown)
        self.signals.rotate.connect(self.rotate)
        self.signals.left.connect(self.moveLeft)
        self.signals.right.connect(self.moveRight)

        self.updateShadow()

    # 切换俄罗斯方块状态
    def to_state(self, x: int, y: int, tetris_type: int, state: int, rectangles: list[Rectangle]):
        if tetris_type == 0:
            if state == 0:
                rectangles[0].setXY(x, y)
                rectangles[1].setXY(x + 1, y)
                rectangles[2].setXY(x + 1, y + 1)
                rectangles[3].setXY(x + 1, y + 2)
            if state == 1:
                rectangles[0].setXY(x + 2, y)
                rectangles[1].setXY(x, y + 1)
                rectangles[2].setXY(x + 1, y + 1)
                rectangles[3].setXY(x + 2, y + 1)
            if state == 2:
                rectangles[0].setXY(x + 1, y)
                rectangles[1].setXY(x + 1, y + 1)
                rectangles[2].setXY(x + 1, y + 2)
                rectangles[3].setXY(x + 2, y + 2)
            if state == 3:
                rectangles[0].setXY(x, y + 1)
                rectangles[1].setXY(x, y + 2)
                rectangles[2].setXY(x + 1, y + 1)
                rectangles[3].setXY(x + 2, y + 1)

        if tetris_type == 1:
            if state == 0 or state == 2:
                rectangles[0].setXY(x, y)
                rectangles[1].setXY(x, y + 1)
                rectangles[2].setXY(x, y + 2)
                rectangles[3].setXY(x, y + 3)
            if state == 1 or state == 3:
                rectangles[0].setXY(x - 1, y + 1)
                rectangles[1].setXY(x, y + 1)
                rectangles[2].setXY(x + 1, y + 1)
                rectangles[3].setXY(x + 2, y + 1)

        if tetris_type == 2:
            if state == 0:
                rectangles[0].setXY(x, y)
                rectangles[1].setXY(x + 1, y)
                rectangles[2].setXY(x, y + 1)
                rectangles[3].setXY(x, y + 2)
            if state == 1:
                rectangles[0].setXY(x - 1, y + 1)
                rectangles[1].setXY(x, y + 1)
                rectangles[2].setXY(x + 1, y + 1)
                rectangles[3].setXY(x + 1, y + 2)
            if state == 2:
                rectangles[0].setXY(x, y)
                rectangles[1].setXY(x, y + 1)
                rectangles[2].setXY(x, y + 2)
                rectangles[3].setXY(x - 1, y + 2)
            if state == 3:
                rectangles[0].setXY(x - 1, y + 1)
                rectangles[1].setXY(x - 1, y + 2)
                rectangles[2].setXY(x, y + 2)
                rectangles[3].setXY(x + 1, y + 2)
        if tetris_type == 3:
            if state == 0:
                rectangles[0].setXY(x, y)
                rectangles[1].setXY(x, y - 1)
                rectangles[2].setXY(x - 1, y)
                rectangles[3].setXY(x + 1, y)
            if state == 1:
                rectangles[0].setXY(x, y)
                rectangles[1].setXY(x, y - 1)
                rectangles[2].setXY(x, y + 1)
                rectangles[3].setXY(x + 1, y)
            if state == 2:
                rectangles[0].setXY(x, y)
                rectangles[1].setXY(x - 1, y)
                rectangles[2].setXY(x + 1, y)
                rectangles[3].setXY(x, y + 1)
            if state == 3:
                rectangles[0].setXY(x, y)
                rectangles[1].setXY(x, y + 1)
                rectangles[2].setXY(x, y - 1)
                rectangles[3].setXY(x - 1, y)

        if tetris_type == 4:
            if state == 0 or state == 2:
                rectangles[0].setXY(x, y)
                rectangles[1].setXY(x - 1, y)
                rectangles[2].setXY(x, y + 1)
                rectangles[3].setXY(x + 1, y + 1)
            if state == 1 or state == 3:
                rectangles[0].setXY(x, y)
                rectangles[1].setXY(x - 1, y)
                rectangles[2].setXY(x, y - 1)
                rectangles[3].setXY(x - 1, y + 1)
        if tetris_type == 5:
            rectangles[0].setXY(x, y)
            rectangles[1].setXY(x + 1, y)
            rectangles[2].setXY(x, y + 1)
            rectangles[3].setXY(x + 1, y + 1)
        if tetris_type == 6:
            if state == 0 or state == 2:
                rectangles[0].setXY(x, y)
                rectangles[1].setXY(x + 1, y)
                rectangles[2].setXY(x, y + 1)
                rectangles[3].setXY(x - 1, y + 1)
            if state == 1 or state == 3:
                rectangles[0].setXY(x, y)
                rectangles[1].setXY(x, y + 1)
                rectangles[2].setXY(x - 1, y)
                rectangles[3].setXY(x - 1, y - 1)

    def to_bottem(self, tetris: list[Rectangle]):
        while self.can_move_down(tetris):
            self.moveDown(tetris)

    def updateShadow(self):
        self.shadow_tetris = [Rectangle(rectangle.x, rectangle.y) for rectangle in self.moving_tetris]
        while self.can_move_down(self.shadow_tetris):
            for rectangle in self.shadow_tetris:
                rectangle.y += 1

    def can_move_down(self, tetris: list[Rectangle]) -> bool:
        for rectangle in tetris:
            if self.isBottomOut(rectangle.y + 1) or self.isCoincide(rectangle.x, rectangle.y + 1, self.dropped_tetris):
                # 重合返回false
                return False
        # 不重合返回true
        return True

    # 判断是否有块出上边界（有的话说明游戏结束了）
    def isUpperBoundaryOut(self) -> bool:
        for rectangle in self.dropped_tetris:
            # 有块出界返回Ture
            if rectangle.y < 0:
                return True
        return False

    # 向下移动一格
    def moveDown(self, tetis: list[Rectangle]):
        # 向下移动之前现有的定时器暂停，移动完了设置新的定时器（因为要实现按下键之后向下移动的定时器重置）
        self.down_timer.stop()
        # 先判断所有的块如果向下移动一格后有没有和已经掉落的块重合或者出界
        for rectangle in tetis:
            if self.isBottomOut(rectangle.y + 1) or self.isCoincide(rectangle.x, rectangle.y + 1, self.dropped_tetris):
                # 重合或者出下解了就直接触发掉落
                self.drop()
                self.down_timer.start(1000)

                # 下落之后判定是否有块出界
                if self.isUpperBoundaryOut():
                    self.endGame()

                return

        # 没重合就下降一格,相对y坐标加一，每一块的y也加一
        self.relative_y += 1
        for rectangle in tetis:
            rectangle.y += 1
        self.down_timer.start(1000)

    # 向左移动的函数,如果移动失败返回false
    def moveLeft(self) -> bool:
        for rectangle in self.moving_tetris:
            if self.isLeftOut(rectangle.x - 1) or self.isCoincide(rectangle.x - 1, rectangle.y, self.dropped_tetris):
                return False
        for rectangle in self.moving_tetris:
            rectangle.x -= 1
        self.relative_x -= 1
        self.updateShadow()
        return True

    # 向右移动的函数,如果移动失败返回false
    def moveRight(self) -> bool:
        for rectangle in self.moving_tetris:
            if self.isRightOut(rectangle.x + 1) or self.isCoincide(rectangle.x + 1, rectangle.y, self.dropped_tetris):
                return False
        for rectangle in self.moving_tetris:
            rectangle.x += 1
        self.relative_x += 1
        self.updateShadow()
        return True

    # 方块变换到下一状态
    def rotate(self):
        # 先尝试旋转一下
        self.to_state(self.relative_x, self.relative_y, self.now_type, self.statePlus(self.state), self.moving_tetris)
        # 先判断有有没有出下界，如果旋转导致出下界则旋转失败
        for rectangle in self.moving_tetris:
            # 如果下面出界就转回来
            if self.isBottomOut(rectangle.y):
                # 转回去
                self.to_state(self.relative_x, self.relative_y, self.now_type, self.state, self.moving_tetris)
                return

        # 向左或者向右挪的长度，有正负，正右
        length = 0
        # 之后判断有没有出左界
        for rectangle in self.moving_tetris:
            if self.isLeftOut(rectangle.x):
                # 如果出了就往右挪一格
                self.moveRight()
                length += 1
                break
        # 再次判断出没出左界
        for rectangle in self.moving_tetris:
            if self.isLeftOut(rectangle.x):
                # 如果还出就再往右挪一格
                self.moveRight()
                length += 1
                break

        # 判断有没有出右界
        for rectangle in self.moving_tetris:
            if self.isRightOut(rectangle.x):
                # 如果出了就往左挪一格
                self.moveLeft()
                length -= 1
        # 再次判断出没出右界
        for rectangle in self.moving_tetris:
            if self.isRightOut(rectangle.x):
                # 如果还出就再往左挪一格
                self.moveRight()
                length -= 1

        for rectangle in self.moving_tetris:
            # 如果重合了那就移动的退回去，之后转回去
            if self.isCoincide(rectangle.x, rectangle.y, self.dropped_tetris):
                if length < 0:
                    for i in range(-length):
                        self.moveRight()
                elif length > 0:
                    for i in range(length):
                        self.moveLeft()
                # 转回去
                self.to_state(self.relative_x, self.relative_y, self.now_type, self.state, self.moving_tetris)
                return
        # 成功旋转后状态加一
        self.state = self.statePlus(self.state)
        self.updateShadow()

    # 方块掉落
    def drop(self):
        # 把正在下落的列表里的方块都加到已掉落数组里面，之后清空下落中数组
        self.dropped_tetris.extend(self.moving_tetris)  # extend是把数组追加数组
        self.moving_tetris.clear()

        # 把下一块列表里的块添加到正在下落的里面，并初始化
        self.moving_tetris.extend(self.next_tetris)

        self.state = 0
        self.now_type = self.next_type
        self.relative_x = 3
        self.relative_y = -2
        self.to_state(self.relative_x, self.relative_y, self.now_type, self.state, self.moving_tetris)

        # 生成一个新的下一块等待
        self.next_tetris.clear()
        self.next_type = random.randint(0, 6)

        color = QColor(random.randint(0, 150), random.randint(0, 150), random.randint(0, 150), 255)
        for i in range(4):
            rectangle = Rectangle(-1, -1)
            rectangle.image.fill(color)

            self.next_tetris.append(rectangle)
        self.to_state(13, 5, self.next_type, 0, self.next_tetris)

        # 如果有整行的就把整行消了，上面的块全部坠落一格
        i = 0
        while i < 20:
            # 这一行要是有10块
            if sum(1 for rectangle in self.dropped_tetris if rectangle.y == i) != 10:
                i += 1
                continue
            # 就移除这一行所有的块
            self.dropped_tetris = [rectangle for rectangle in self.dropped_tetris if rectangle.y != i]

            # 把这一行上面的所有的块的往下移一格
            for rectangle in self.dropped_tetris:
                if rectangle.y < i:
                    rectangle.y += 1
        self.updateShadow()

    # 判断某xy坐标与某方块列表里面的任意方块是否重合
    # 重合了就返回ture
    # 不重合就返回false
    def isCoincide(self, x: int, y: int, rectangles: list[Rectangle]) -> bool:
        for rectangle in rectangles:
            if x == rectangle.x and y == rectangle.y:
                return True
        return False

    # 判断所有的块有没有出下界，如果有出的返回True
    def isBottomOut(self, y: int) -> bool:
        if y > 19:
            return True
        return False

    # 判断所有的块有没有出左界，如果有出的返回True
    def isLeftOut(self, x: int) -> bool:
        if x < 0:
            return True
        return False

    # 判断所有的块有没有出右界，如果有出的返回True
    def isRightOut(self, x) -> bool:
        if x > 9:
            return True
        return False

    # 完全是状态码加减，不涉及方块状态变换
    # 状态减一，[0,3] 0-1 = 3
    def stateReduce(self, state: int) -> int:
        state -= 1
        if state < 0:
            state = 3
        return state

    # 状态加一，3+1=0
    def statePlus(self, state: int) -> int:
        state += 1
        if state > 3:
            state = 0
        return state

    # 状态减一
    def stateSub(self, state: int) -> int:
        state -= 1
        if state < 0:
            state = 3
        return state

    def initUI(self):
        self.setWindowTitle("Main Window")
        self.setGeometry(0, 0, 350, 470)
        # 创建一个按钮并设置其位置和大小
        pause = QPushButton('pause', self)
        pause.setGeometry(240, 200, 100, 30)  # x, y, width, height
        pause.clicked.connect(self.pauseGame)
        # 设置按钮不获取焦点
        pause.setFocusPolicy(Qt.NoFocus)

        continue_button = QPushButton('continue', self)
        continue_button.setGeometry(240, 230, 100, 30)  # x, y, width, height
        continue_button.clicked.connect(self.continueGame)
        # 设置按钮不获取焦点
        continue_button.setFocusPolicy(Qt.NoFocus)

    def keyHandle(self):
        if self.left_pressing:
            if self.left_press_i == 0 or self.left_press_i == x_press_i:
                self.signals.left.emit()
            if self.left_press_i > 0:
                self.left_press_i -= 1

        if self.right_pressing:
            if self.right_press_i == 0 or self.right_press_i == x_press_i:
                self.signals.right.emit()
            if self.left_press_i > 0:
                self.left_press_i -= 1

        if self.down_pressing:
            self.signals.down.emit(self.moving_tetris)

        if self.rotate_first_pressed:
            self.signals.rotate.emit()
            self.rotate_first_pressed = False
            print("rotate")

        if self.bottom_first_pressed:
            self.signals.to_bottom.emit(self.moving_tetris)
            self.bottom_first_pressed = False

    # 处理引脚上面的按钮按下
    def pinButtonTimer(self):
        # 按下处理
        if chip0_rotate.get_value() == 0:
            if self.rotate_pressing == False:
                self.rotate_first_pressed = True
                self.rotate_pressing = True
            print("rotate pressing")
        if chip0_down.get_value() == 0:
            print("down pressing")
            self.down_pressing = True
        # 如果之前没有按下说明是第一次按下，不是按下之后重复发出的事件
        if chip3_left.get_value() == 0 and not self.left_pressing:
            self.left_pressing = True
            self.left_press_i = x_press_i
        if chip3_right.get_value() == 0 and not self.right_pressing:
            self.right_pressing = True
            self.right_press_i = x_press_i
        if chip3_to_bottom.get_value() == 0 and not self.bottom_pressing:
            self.bottom_pressing = True
            self.bottom_first_pressed = True

        # 抬起处理
        if chip0_rotate.get_value() == 1:
            self.rotate_first_pressed = False
            self.rotate_pressing = False
        if chip0_down.get_value() == 1:
            self.down_pressing = False
        if chip3_left.get_value() == 1:
            self.left_press_i = 0
            self.left_pressing = False
        if chip3_right.get_value() == 1:
            self.right_press_i = 0
            self.right_pressing = False
        if chip3_to_bottom.get_value() == 1:
            self.bottom_pressing = False
            self.bottom_first_pressed

    # 键盘事件处理
    def keyPressEvent(self, event):
        if event.isAutoRepeat():
            return
        print("event")

        if event.key() == Qt.Key_Left:
            self.left_pressing = x_press_i
            self.left_pressing = True

        elif event.key() == Qt.Key_Right:
            self.right_press_i = x_press_i
            self.right_pressing = True

        elif event.key() == Qt.Key_Up:
            self.rotate_first_pressed = True
            self.rotate_pressing = True

        elif event.key() == Qt.Key_Down:
            self.down_pressing = True

        elif event.key() == Qt.Key_Space:
            self.bottom_first_pressed = True
            self.bottom_pressing = True

        # 键盘抬起事件处理

    def keyReleaseEvent(self, event):
        if event.isAutoRepeat():
            return

        print("Key released")
        if event.key() == Qt.Key_Left:
            self.left_pressing = False

        elif event.key() == Qt.Key_Right:
            self.right_pressing = False

        elif event.key() == Qt.Key_Up:
            print("release")
            self.rotate_pressing = False
            self.rotate_first_pressed = False

        elif event.key() == Qt.Key_Down:
            self.down_pressing = False

        elif event.key() == Qt.Key_Space:
            self.bottom_pressing = False
            self.bottom_first_pressed = False

    def pauseGame(self):
        self.down_timer.stop()
        self.key_timer.stop()
        if not platform.system() == "Windows":
            self.pin_button_timer.stop()
        self.gameState = 1

    def continueGame(self):
        self.down_timer.start()
        self.key_timer.start()
        if not platform.system() == "Windows":
            self.pin_button_timer.start()
        self.gameState = 0

    def endGame(self):
        self.down_timer.start()
        self.key_timer.start()
        if not platform.system() == "Windows":
            self.pin_button_timer.start()
        self.gameState = 2

    def paintEvent(self, event):
        painter = QPainter(self)
        if self.gameState == 0 or self.gameState == 1:  # 如果游戏进行中绘制游戏相关内容

            # 绘制背景
            painter.setPen(QColor("#9da58c"))
            painter.fillRect(0, 0, 1000, 1000, QColor("#9da58c"))

            # 绘制边框
            painter.setPen(QColor("#d100ff"))
            painter.drawRect(0, 0, 233, 463)

            for i in range(10):
                for z in range(20):
                    painter.fillRect(i * 23 + 3, z * 23 + 3, 20, 20, QColor("#90997e"))

            # 绘制已掉落的方块
            for rectangle in self.dropped_tetris:
                painter.drawImage(rectangle.get_absolute_position(), rectangle.show())

            # 绘制下落位置预测方块
            for rectangle in self.shadow_tetris:
                painter.fillRect(rectangle.get_absolute_position().x(), rectangle.get_absolute_position().y(), 20, 20,
                                 QColor(100, 100, 100))
                
            # 绘制下落中方块
            for rectangle in self.moving_tetris:
                painter.drawImage(rectangle.get_absolute_position(), rectangle.show())

            # 绘制下一个准备来的方块
            for rectangle in self.next_tetris:
                painter.drawImage(rectangle.get_absolute_position(), rectangle.show())

        if self.gameState == 1:  # 游戏暂停中
            painter.drawText(100, 100, 100, 20, 0, "暂停中")
        elif self.gameState == 2:  # 游戏结束
            painter.drawText(100, 100, 100, 20, 0, "游戏结束")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())
